package cn.jx.cjm.common.aop.advice;

import cn.jx.cjm.common.base.BaseRequest;
import cn.jx.cjm.common.base.MDCUser;
import cn.jx.cjm.common.util.JwtTokenUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * controler 方法进入 日志打印
 *
 * @author James Chen right_way@foxmail.com
 * @since 2018年9月30日 下午5:11:56
 */
@Aspect
@Component
@Slf4j
public class LogInterceptor {

    private static final String ADMIN_TOKEN_HEADER = "Auth-Token";
    private static final String USER_TOKEN_HEADER = "X-Auth-Token";

    @Pointcut(value = "@annotation(io.swagger.annotations.ApiOperation)")
    public void cutService() {
    }

    @Before("cutService()")
    public void doBefore(JoinPoint point) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        try {
            String mdcTraceId;
            if (!StringUtils.isEmpty((mdcTraceId = request.getHeader(ADMIN_TOKEN_HEADER)))) {
                MDCUser mdcUser = JwtTokenUtils.parse(mdcTraceId, MDCUser.class);
                mdcTraceId = null == mdcUser ? mdcTraceId.substring(mdcTraceId.length() - 32) : (mdcUser.getRealName() + " - " + mdcUser.getId());
            } else if (!StringUtils.isEmpty((mdcTraceId = request.getHeader(USER_TOKEN_HEADER)))) {
                Object user = request.getSession().getAttribute("user");
                if (user != null) {
                    JSONObject userInfo = JSON.parseObject(JSON.toJSONString(user));
                    mdcTraceId = userInfo.getString("name") + " - " + userInfo.getInteger("id");
                }
            } else {
                mdcTraceId = "";
            }
            MDC.put("mdcTraceId", mdcTraceId);

            MethodSignature msig = (MethodSignature) point.getSignature();
            Object target = point.getTarget();
            Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
            Object[] params = point.getArgs();
            ApiOperation annotation = currentMethod.getAnnotation(ApiOperation.class);
            String apiOperationName = annotation.value();
            StringBuilder sb = new StringBuilder();
            for (Object param : params) {
                if (!(param instanceof HttpServletRequest) && !(param instanceof HttpServletResponse)) {
                    if (param instanceof BaseRequest) {
                        sb.append(param.toString());
                    } else {
                        sb.append(JSON.toJSONString(param, SerializerFeature.IgnoreErrorGetter));
                    }
                }

            }
            log.info("{}-开始,参数{}", apiOperationName, sb.toString());
        } catch (Exception e) {
            log.error("日志记录出错!", e);
        }

    }

    @Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
        Object result = point.proceed();
        try {
            MethodSignature msig = (MethodSignature) point.getSignature();
            Object target = point.getTarget();
            Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
            ApiOperation annotation = currentMethod.getAnnotation(ApiOperation.class);
            String apiOperationName = annotation.value();
            if (result != null) {
                log.info("{}-结束,返回结果:{}", apiOperationName, JSON.toJSONString(result, SerializerFeature.IgnoreErrorGetter));
            } else {
                log.info("{}-结束,", apiOperationName);
            }
        } catch (Exception e) {
            log.error("日志记录出错!", e);
        } finally {
            MDC.clear();
        }
        return result;
    }
}
